/* -*- c++ -*-
   convert_functions.cc - LLVM-IR to GCC conversion - functions.
   Copyright (C) 2011 Matthew Wahab <mwb.cde@gmail.com>

   This file is part of GCC-LLVMIR.

   GCC-LLVMIR is free software; you can redistribute it and/or modify it under
   the terms of the GNU General Public License as published by the Free
   Software Foundation; either version 3, or (at your option) any later
   version.

   GCC-LLVMIR is distributed in the hope that it will be useful, but WITHOUT ANY
   WARRANTY; without even the implied warranty of MERCHANTABILITY or
   FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
   for more details.

   You should have received a copy of the GNU General Public License
   along with GCC-LLVMIR; see the file COPYING3.  If not see
   <http://www.gnu.org/licenses/>.  */

/* Conversion code for LLVM IR functions, function calls and related items. */

#include "llvmir.h"
#include "parser.h"

#include "llvm/Type.h"
#include "llvm/DerivedTypes.h"
#include "llvm/GlobalValue.h"
#include "llvm/InlineAsm.h"
#include "llvm/Function.h"
#include "llvm/BasicBlock.h"
#include "llvm/IntrinsicInst.h"
#include "llvm/Instruction.h"
#include "llvm/Instructions.h"
#include "llvm/Constants.h"
#include "llvm/ADT/APInt.h"
#include "llvm/ADT/APFloat.h"
#include "llvm/ADT/SmallString.h"
#include "llvm/Support/DebugLoc.h"
#include "llvm/Support/raw_os_ostream.h"

namespace llvmir
{
  /*
   * Support functions.
   */

  /* Builder for an TerminalInstrRepr. */
  tree TerminalInstrRepr::build()
  {
    tree stmt_list = extra_instrs_;
    append_to_statement_list(instrs_, &stmt_list);
    return stmt_list;
  }

  /* Builder for an InvokeInstrRepr. */
  tree InvokeInstrRepr::build()
  {
    tree try_stmt_list = NULL_TREE;
    tree instr_tree = NULL_TREE;

    // CALL function.
    append_to_statement_list(this->call_stmt, &try_stmt_list);
    // Any extra code.
    append_to_statement_list(this->extra_instrs_, &try_stmt_list);
    // GOTO normal.
    append_to_statement_list(this->goto_normal, &try_stmt_list);

    instr_tree = fold_build2_loc(this->loc,
                                 TRY_CATCH_EXPR, 
                                 this->expr_type,
                                 try_stmt_list,
                                 this->goto_exception);

    return instr_tree;
  }

  /*
   * Types
   */

  /* Convert an LLVM function type. */
  TypeRepr*
  Parser::convert_function_type(Context *context,
                                const llvm::Type* llvm_type)
  {
    const llvm::FunctionType *llvm_fn_type = 
      static_cast<const llvm::FunctionType*>(llvm_type);
    
    tree fn_type = NULL_TREE;
    tree ret_type = NULL_TREE;
    tree arg_type_list = NULL_TREE;
    tree last_arg = NULL_TREE;
    TypeRepr* fn_ret_repr = 0;
    TypeRepr* fn_type_repr = 0;

    // Convert the return type.
    fn_ret_repr = convert_type(context, llvm_fn_type->getReturnType());
    gcc_assert(fn_ret_repr);

    ret_type = fn_ret_repr->get_tree();
    gcc_assert(ret_type);

    // Convert the argument list, terminated with void_list_node.
    // Variadic arguements are not terminated with void_list_node.
    arg_type_list = NULL_TREE;
    for (llvm::FunctionType::param_iterator 
           itr = llvm_fn_type->param_begin(),
           end = llvm_fn_type->param_end();
         itr != end;
         ++ itr)
      {
        const llvm::Type* llvm_param_type = *itr;
        TypeRepr* param_repr = convert_type(context, llvm_param_type);
        gcc_assert(param_repr && param_repr->get_tree());
        tree param_type = param_repr->get_tree();

        arg_type_list = tree_cons(NULL_TREE, param_type, arg_type_list);
      }
    last_arg = arg_type_list;
    if (arg_type_list != NULL_TREE)
      nreverse(arg_type_list);

    // Non-Variadic arguments are terminated with void_list_node.
    if (last_arg && !llvm_fn_type->isVarArg())
      TREE_CHAIN(last_arg) = void_list_node;

    // Build the function type.
    fn_type = build_function_type(ret_type, arg_type_list);

    fn_type_repr = new FuncTypeRepr(llvm_fn_type, fn_type);
    context->register_type_repr(fn_type_repr);

    return fn_type_repr;
  }

  /*
   * Values
   */

  /* Convert an LLVM value identifying a function. */
  ValueRepr* 
  Parser::convert_function_value(Context *context, 
                                 const llvm::Value *llvm_fnval)
  {
    // Sanity checks.
    gcc_assert(llvm_fnval);
    gcc_assert(context && context->curr_context);

    ValueRepr* fn_repr = 0;

    // The function should already have been declared and recorded in
    // the global scope.
    fn_repr = context->lookup_value(llvm_fnval);

    if (!fn_repr)
      {
        error_at(context->curr_context->src_loc,
                 "unknown function");
        gcc_unreachable();
      }

    return fn_repr;
  }

  /* Convert an LLVM parameter. */
  ValueRepr*
  Parser::convert_parameter(Context *context, 
                            const llvm::Value* llvm_value)
  {
    FnContext* const fn_context = 
      FnContext::from(context->curr_context);
    const llvm::Argument* llvm_param =
      static_cast<const llvm::Argument*>(llvm_value);
    gcc_assert(llvm_param);
    const llvm::Type* llvm_type = llvm_param->getType();
    TypeRepr* type_repr = 0;
    tree type_tree = NULL_TREE;
    tree param_tree = NULL_TREE;
    std::string llvm_name;
    location_t param_loc = UNKNOWN_LOCATION;
    ValueRepr* param_repr = 0;

    param_repr = context->lookup_value(llvm_param);
    if (param_repr && param_repr->get_tree())
      return param_repr;

    type_repr = convert_type(context, llvm_type);
    type_tree = type_repr->get_tree();
    param_tree = build_decl(param_loc, PARM_DECL, NULL_TREE, type_tree);

    DECL_ARG_TYPE(param_tree) = type_tree;

    llvm_name = llvm_param->getName();
    DECL_NAME(param_tree) = Tree::make_name_tree(llvm_name, false);

    if (param_tree == NULL_TREE)
      return 0;

    // If the function is available, set the declaration context.
    if (fn_context && fn_context->fn_repr)
      DECL_CONTEXT(param_tree) = fn_context->fn_repr->get_decln();

    param_repr = new ValueRepr(llvm_param, param_tree);
    fn_context->register_value_repr(param_repr);
    return param_repr;
  }

  /*
   * Instructions
   */

  /* Convert a return instruction. */
  InstrRepr*
  Parser::convert_instr_ret(Context *context,
                            const llvm::Instruction *llvm_instr)
  {
    /*
     * [| RET |] --> RETURN_EXPR
     *
     * [| RET expr |] 
     * -->
     * RETURN_EXPR (MODIFY_EXPR <fn-result> [| expr |])
     */
    FnContext* const curr_context = FnContext::from(context->curr_context);
    location_t instr_loc =
      convert_location(context, &(llvm_instr->getDebugLoc()));
    const llvm::ReturnInst* llvm_ret = 
      static_cast<const llvm::ReturnInst*>(llvm_instr);
    FunctionRepr* fn_repr = curr_context->fn_repr;
    FuncTypeRepr* fn_type = fn_repr->fn_type;
    tree instr_set_result = NULL_TREE;
    tree instr_tree = NULL_TREE;
    InstrRepr* instr_repr = 0;
    tree fn_ret_type = NULL_TREE;
    const llvm::Value* llvm_ret_val = llvm_ret->getReturnValue();

    curr_context->src_loc = instr_loc;

    // Get and assign the function return value, if it has one.
    if (llvm_ret_val)
      {
        tree ret_value = NULL_TREE;
        ValueRepr* ret_val_repr = convert_value(context, llvm_ret_val);

        gcc_assert(fn_repr->result);
        gcc_assert(fn_repr->result->get_tree());
        ret_value = ret_val_repr->get_tree();

        instr_set_result = fold_build2_loc(instr_loc, 
                                           MODIFY_EXPR, void_type_node,
                                           fn_repr->result->get_tree(),
                                           ret_value);
      }

    // Form the return instruction.

    // Get the return type from the function representation.
    gcc_assert(fn_type);
    if (fn_type)
      {
        gcc_assert(curr_context->fn_repr->fn_type->get_kind() == TypeRepr::FUNC);
        const llvm::FunctionType* llvm_func_type = fn_type->get_fn_type();
        const llvm::Type* llvm_return_type = llvm_func_type->getReturnType();
        TypeRepr* return_type = convert_type(context, llvm_return_type);
        gcc_assert(return_type && return_type->get_tree());
        fn_ret_type = return_type->get_tree();
      }
    else
      fn_ret_type = TREE_TYPE(fn_type->get_tree());

    instr_tree = fold_build1_loc(instr_loc, RETURN_EXPR, 
                                 fn_ret_type, instr_set_result);

    instr_repr = new TerminalInstrRepr(llvm_ret, instr_tree, instr_tree);
    curr_context->register_value_repr(instr_repr);

    return instr_repr;
  }

  /* Build a function call from a converted function value and
     arguments, casting the arguments to the proper type. */
  tree
  Parser::build_function_call(Context *context ATTRIBUTE_UNUSED,
                              location_t loc,
                              TypeRepr* type_repr,
                              ValueRepr* fnptr_repr,
                              ValueReprSeq* arg_repr_seq,
                              const bool is_variadic ATTRIBUTE_UNUSED)
  {
    gcc_assert(fnptr_repr && fnptr_repr->get_tree());

    tree* arg_seq_tree = 0;
    tree fnptr_type_tree = NULL_TREE;
    tree fntype_tree = NULL_TREE;
    tree arg_type_list = NULL_TREE;
    tree call_tree = NULL_TREE;
    const unsigned num_args = arg_repr_seq ? (arg_repr_seq->size()) : 0;

    // Get the function type.
    fnptr_type_tree = TREE_TYPE(fnptr_repr->get_tree());
    gcc_assert(TREE_CODE(fnptr_type_tree) == POINTER_TYPE);

    fntype_tree = TREE_TYPE(fnptr_type_tree);
    gcc_assert(TREE_CODE(fntype_tree) == FUNCTION_TYPE);

    // Convert the arguments.
    if (num_args)
      arg_seq_tree = new tree[num_args];

    arg_type_list = TYPE_ARG_TYPES(fntype_tree);
    for (unsigned idx = 0; idx < num_args; idx += 1)
      {
        tree arg_type_tree = NULL_TREE;

        if (arg_type_list)
          {
            arg_type_tree = TREE_VALUE(arg_type_list);
            arg_type_list = TREE_CHAIN(arg_type_list);
          }

        if ((*arg_repr_seq)[idx] == 0)
          continue;

        if (arg_type_tree)
          {
            tree cast_tree = 
              fold_build1(CONVERT_EXPR, arg_type_tree,
                          (*arg_repr_seq)[idx]->get_tree());
            arg_seq_tree[idx] = cast_tree;
          }
        else
          {
            if (context->options->debug_output)
              {
                warning_at(loc, 0,
                           "type mismatch in function call - excess operand.");
              }
            arg_seq_tree[idx] = (*arg_repr_seq)[idx]->get_tree();
          }
      }

    // Build: CALL_EXPR fnptr (arg1, ..., argn)
    call_tree = build_call_array_loc(loc,
                                     type_repr->get_tree(),
                                     fnptr_repr->get_tree(),
                                     num_args,
                                     arg_seq_tree);
    delete [](arg_seq_tree); 
    arg_seq_tree = 0;

    // Done.
    return call_tree;
  }

  /* Convert a call to an LLVM intrinsic function. Return 0 if the
     function call is not handled as a special case. */
  tree
  Parser::convert_call_intrinsic(Context *context,
                                 const llvm::CallInst* llvm_call,
                                 const llvm::Type* llvm_type,
                                 const llvm::Value* llvm_fnptr)
  {
    gcc_assert(llvm_call);
    gcc_assert(context && context->curr_context);
    const llvm::Function* llvm_function = 0;
    location_t instr_loc = 
      convert_location(context, &(llvm_call->getDebugLoc()));
    ValueRepr* fnptr_repr = 0;
    TypeRepr *type_repr = 0;
    ValueReprSeq arg_repr_seq;
    tree call_tree = NULL_TREE;

    // Test whether the called function is an intrinsic.
    if (!llvm::isa<llvm::IntrinsicInst>(*llvm_call))
      return NULL_TREE;

    // Convert the return type.
    type_repr = convert_type(context, llvm_type);

    gcc_assert(llvm::isa<const llvm::Function>(*llvm_fnptr));
    llvm_function = static_cast<const llvm::Function*>(llvm_fnptr);

    gcc_assert(llvm_function->isIntrinsic());
    switch(llvm_function->getIntrinsicID())
      {
        // Zero argument, simply mapped functions.
      case llvm::Intrinsic::trap:
      case llvm::Intrinsic::stacksave:
        {
          // Get or make the function declaration.
          fnptr_repr = make_intrinsic_decln(context, llvm_function);
          if (!fnptr_repr)
            return NULL_TREE;

          call_tree = build_function_call(context, instr_loc,
                                          type_repr, fnptr_repr,
                                          &arg_repr_seq, false);
          return call_tree;
        }
        // One argument, simply mapped functions.
      case llvm::Intrinsic::vastart:
      case llvm::Intrinsic::vaend:
      case llvm::Intrinsic::returnaddress:
      case llvm::Intrinsic::frameaddress:
      case llvm::Intrinsic::stackrestore:
      case llvm::Intrinsic::sqrt:
      case llvm::Intrinsic::sin:
      case llvm::Intrinsic::cos:
      case llvm::Intrinsic::exp:
      case llvm::Intrinsic::log:
      case llvm::Intrinsic::ctlz:
      case llvm::Intrinsic::cttz:
        {
          // Get or make the function declaration.
          fnptr_repr = make_intrinsic_decln(context, llvm_function);
          if (!fnptr_repr)
            return NULL_TREE;

          arg_repr_seq.resize(1);
          arg_repr_seq[0] = convert_value(context, llvm_call->getArgOperand(0));

          call_tree = build_function_call(context, instr_loc,
                                          type_repr, fnptr_repr,
                                          &arg_repr_seq, false);
          return call_tree;
        }
        // Two argument, simply mapped functions.
      case llvm::Intrinsic::objectsize:
      case llvm::Intrinsic::vacopy:
      case llvm::Intrinsic::powi:
      case llvm::Intrinsic::pow:
        {
          // Get or make the function declaration.
          fnptr_repr = make_intrinsic_decln(context, llvm_function);
          if (!fnptr_repr)
            return NULL_TREE;

          arg_repr_seq.resize(3);
          arg_repr_seq[0] = convert_value(context, llvm_call->getArgOperand(0));
          arg_repr_seq[1] = convert_value(context, llvm_call->getArgOperand(1));

          call_tree = build_function_call(context, instr_loc,
                                          type_repr, fnptr_repr,
                                          &arg_repr_seq, false);
          return call_tree;
        }
        // Three argument, simply mapped functions.
      case llvm::Intrinsic::memset:
      case llvm::Intrinsic::memcpy:
      case llvm::Intrinsic::memmove:
      case llvm::Intrinsic::prefetch:
        {
          // Get or make the function declaration.
          fnptr_repr = make_intrinsic_decln(context, llvm_function);
          if (!fnptr_repr)
            return NULL_TREE;

          arg_repr_seq.resize(3);
          arg_repr_seq[0] = convert_value(context, llvm_call->getArgOperand(0));
          arg_repr_seq[1] = convert_value(context, llvm_call->getArgOperand(1));
          arg_repr_seq[2] = convert_value(context, llvm_call->getArgOperand(2));

          call_tree = build_function_call(context, instr_loc,
                                          type_repr, fnptr_repr,
                                          &arg_repr_seq, false);
          return call_tree;
        }
        // Ignored intrinsics.
      case llvm::Intrinsic::gcroot:
      case llvm::Intrinsic::gcread:
      case llvm::Intrinsic::gcwrite:
      case llvm::Intrinsic::annotation:
      case llvm::Intrinsic::var_annotation:
      case llvm::Intrinsic::pcmarker:
      case llvm::Intrinsic::readcyclecounter:
      case llvm::Intrinsic::lifetime_start:
      case llvm::Intrinsic::lifetime_end:
      case llvm::Intrinsic::invariant_start:
      case llvm::Intrinsic::invariant_end:
        {
          call_tree = fold_build1_loc(instr_loc, 
                                      NOP_EXPR, void_type_node,
                                      NULL_TREE);
          return call_tree;
        }
      default:
        break;
      }
    return call_tree;
  }

  /* Convert a call to a GCC built-in function. Return 0 if the
     function call is not handled as a special case. */
  tree
  Parser::convert_call_builtin(Context *context ATTRIBUTE_UNUSED,
                               const llvm::Instruction* llvm_instr ATTRIBUTE_UNUSED,
                               const llvm::Type* llvm_type ATTRIBUTE_UNUSED,
                               const llvm::Value* llvm_fnptr ATTRIBUTE_UNUSED)
  {
    // By default, GCC built-ins are treated as normal functions.
    return NULL_TREE;
  }

  /* Construct function calls for convert_instr_call and
     convert_instr_invoke. */
  tree
  Parser::convert_function_call(Context *context,
                                const llvm::Instruction* llvm_instr,
                                const llvm::Value* llvm_fnptr,
                                const unsigned num_args)
  {
    /*
     * Function call
     * --------
     * [| <fn-ty>* <fnptr> ( <arg1>, .., <argn> ) |] 
     * -->
     * CALL_EXPR [| fnptr |] ([| arg1 |], ..., [| argn |])
     */
    gcc_assert(llvm_instr);
    gcc_assert(llvm_fnptr);

    ObjContext* const curr_context = context->curr_context;
    location_t instr_loc = 
      convert_location(context, &(llvm_instr->getDebugLoc()));
    const llvm::Type* llvm_type = llvm_instr->getType();
    const llvm::Type* llvm_fnptr_type = llvm_fnptr->getType();
    bool is_variadic = false;
    TypeRepr* type_repr = 0;
    ValueRepr* fnptr_repr = 0;
    ValueReprSeq arg_repr_seq;
    tree call_tree = NULL_TREE;

    // Handle general calls.
    curr_context->src_loc = instr_loc;

    llvm_type = llvm_instr->getType();
    gcc_assert(llvm_type);

    llvm_fnptr_type = llvm_fnptr->getType();
    if (llvm_fnptr_type)
      {
        gcc_assert(llvm::isa<llvm::PointerType>(*llvm_fnptr_type));
        const llvm::PointerType* llvm_ptr_type = 
          static_cast<const llvm::PointerType*>(llvm_fnptr_type);
        const llvm::Type* llvm_tmp_type = llvm_ptr_type->getElementType();
        gcc_assert(llvm::isa<llvm::FunctionType>(*llvm_tmp_type));
        const llvm::FunctionType* llvm_fnptr_type = 
          static_cast<const llvm::FunctionType*>(llvm_tmp_type);

        // Test for a call to a variadic function.
        is_variadic = llvm_fnptr_type->isVarArg();
      }

    // Convert the function pointer and type
    fnptr_repr = convert_value(context, llvm_fnptr);
    type_repr = convert_type(context, llvm_type);

    gcc_assert(fnptr_repr && fnptr_repr->get_tree());
    gcc_assert(type_repr && type_repr->get_tree());
      
    // Convert the arguments
    arg_repr_seq.resize(num_args);
    for (unsigned idx = 0; idx < num_args; idx += 1)
      {
        const llvm::Value* llvm_arg = llvm_instr->getOperand(idx);
        
        if (!llvm_arg)
          continue;

        arg_repr_seq[idx] = convert_value(context, llvm_arg);
        gcc_assert(arg_repr_seq[idx] && arg_repr_seq[idx]->get_tree());
      }

    // Build the call
    call_tree = build_function_call(context, instr_loc,
                                    type_repr, fnptr_repr,
                                    &arg_repr_seq, is_variadic);

    // Done.
    return call_tree;
  }

  /* Convert a call instruction. */
  InstrRepr* 
  Parser::convert_instr_call(Context *context,
                             const llvm::Instruction* llvm_instr)
  {
    /*
     * Function calls.
     * --------
     * [| call void <fn-ty>* <fnptr> ( <arg1>, .., <argn> ) |] 
     * -->
     * CALL_EXPR [| fnptr |] ([| arg1 |], ..., [| argn |])
     *
     * [| call <ret-ty> <fn-ty>* <fnptr> ( <arg1>, .., <argn> ) |] 
     * -->
     * SAVE_EXPR (CALL_EXPR [| fnptr |] ([| arg1 |], ..., [| argn |]))
     *
     * Note that in (CALL_EXPR f (args)), the called function f is a
     * pointer to a function.
     */
    gcc_assert(llvm_instr);
    ObjContext* const curr_context = context->curr_context;
    location_t instr_loc = 
      convert_location(context, &(llvm_instr->getDebugLoc()));
    const llvm::CallInst* llvm_call = 
      static_cast<const llvm::CallInst*>(llvm_instr);
    const llvm::Type* llvm_type = 0;
    const llvm::Value* llvm_fnptr = 0;
    unsigned num_args = 0;
    InstrRepr* instr_repr = 0;
    tree call_tree = NULL_TREE;
    tree instr_tree = NULL_TREE;

    curr_context->src_loc = instr_loc;

    llvm_type = llvm_call->getType();
    llvm_fnptr = llvm_call->getCalledValue();
    num_args = llvm_call->getNumArgOperands();

    gcc_assert(llvm_fnptr);
    gcc_assert(llvm_type);
    
    // LLVM Inline ASM expresssions are represented as a type of call.
    if (llvm::isa<const llvm::InlineAsm>(*llvm_fnptr))
      return convert_instr_asm(context, llvm_instr);

    // Handle LLVM intrinsic special cases.
    call_tree = convert_call_intrinsic(context, llvm_call,
                                       llvm_type, llvm_fnptr);


    // Handle GCC built-ins special cases.
    if (!call_tree)
      call_tree = convert_call_builtin(context, llvm_instr,
                                       llvm_type, llvm_fnptr);

    // Build a generic function call.
    if (!call_tree)
      call_tree = convert_function_call(context, llvm_call,
                                        llvm_fnptr, num_args);
    gcc_assert(call_tree);

    // Save the result of the call.
    if (!llvm_type->isVoidTy())
      instr_tree = build1(SAVE_EXPR, TREE_TYPE(call_tree), call_tree);
    else 
      instr_tree = call_tree;

    // Set properties of the call.
    if (TREE_CODE(call_tree) == CALL_EXPR)
      {
#ifndef LLVMIR_GCC_TRUNK
        if (llvm_call->isNoInline())
          CALL_CANNOT_INLINE_P(call_tree);
#endif // LLVMIR_GCC_TRUNK

        if (llvm_call->isTailCall())
          CALL_EXPR_TAILCALL(call_tree);
      }

    // Done.
    instr_repr = new InstrRepr(llvm_instr, instr_tree, instr_tree);
    curr_context->register_value_repr(instr_repr);
    return instr_repr;
  }

  /* Convert an invoke instruction. */
  InstrRepr* 
  Parser::convert_instr_invoke(Context *context,
                               const llvm::Instruction* llvm_instr)
  {
    /*
     * Function invocation
     * --------
     * [| 
     *    invoke <attrs> <fn-ty>* <fnptr> ( <arg1>, .., <argn> ) 
     *    to label <next> 
     *    unwind label <excp>
     * |] 
     * -->
     * TRY_CATCH_EXPR
     *   {
     *     CALL_EXPR [| fnptr |] ([| arg1 |], ..., [| argn |])
     *     GOTO [| next |]
     *   },
     *   {
     *     GOTO_EXPR [| excp |]
     *   }
     */
    gcc_assert(llvm_instr);
    ObjContext* const curr_context = context->curr_context;
    location_t instr_loc = 
      convert_location(context, &(llvm_instr->getDebugLoc()));
    const llvm::InvokeInst* llvm_invoke = 
      static_cast<const llvm::InvokeInst*>(llvm_instr);
    const llvm::Type* llvm_type = 0;
    const llvm::Value* llvm_fnptr = 0;
    const llvm::Value* llvm_excp = 0;
    const llvm::Value* llvm_next = 0;
    unsigned num_args = 0;
    TypeRepr* type_repr = 0;
    ValueRepr* next_repr = 0;
    ValueRepr* excp_repr = 0;
    tree goto_normal_tree = NULL_TREE;
    tree goto_excp_tree = NULL_TREE;
    tree call_tree = NULL_TREE;
    tree stmt_list = NULL_TREE;
    InvokeInstrRepr* instr_repr = 0;

    // Handle general calls.
    curr_context->src_loc = instr_loc;

    llvm_fnptr = llvm_invoke->getCalledValue();
    num_args = llvm_invoke->getNumArgOperands();

    llvm_type = llvm_invoke->getType();
    gcc_assert(llvm_type);
 
    llvm_next = llvm_invoke->getNormalDest();
    llvm_excp = llvm_invoke->getUnwindDest();

    // Convert the main operands
    type_repr = convert_type(context, llvm_type);
    excp_repr = convert_value(context, llvm_excp);
    next_repr = convert_value(context, llvm_next);

    gcc_assert(type_repr && type_repr->get_tree());
    gcc_assert(excp_repr && excp_repr->get_tree());
    gcc_assert(next_repr && next_repr->get_tree());

    // Build: CALL_EXPR fnptr (arg1, ..., argn)
    call_tree = convert_function_call(context, llvm_instr, 
                                      llvm_fnptr, num_args);
    gcc_assert(call_tree);

    // Set properties of the call.
#ifndef LLVMIR_GCC_TRUNK
    if (llvm_invoke->isNoInline())
      CALL_CANNOT_INLINE_P(call_tree);
#endif // LLVMIR_GCC_TRUNK


    // Save the result of the call.
    if (!llvm_type->isVoidTy())
      call_tree = build1(SAVE_EXPR, TREE_TYPE(call_tree), call_tree);

    // Build: GOTO_EXPR next
    goto_normal_tree = fold_build1_loc(instr_loc,
                                       GOTO_EXPR, type_repr->get_tree(),
                                       next_repr->get_tree());

    // Build: GOTO_EXPR excp
    goto_excp_tree = fold_build1_loc(instr_loc,
                                     GOTO_EXPR, type_repr->get_tree(),
                                     excp_repr->get_tree());

    // Build the representation. The final tree will be built when the
    // function is converted.
    instr_repr = new InvokeInstrRepr(llvm_instr, call_tree, stmt_list);

    instr_repr->loc = instr_loc;
    instr_repr->call_stmt = call_tree;
    instr_repr->goto_normal = goto_normal_tree;
    instr_repr->goto_exception = goto_excp_tree;

    // Done.
    curr_context->register_value_repr(instr_repr);
    return instr_repr;
  }

  /*
   * Basic blocks
   */

  /* Create and set-up the representation for a basic block. This generates the
     representation and the label declaration for the block.
  */
  BlockRepr*
  Parser::setup_block_repr(Context *context,
                           const llvm::BasicBlock *llvm_block,
                           const bool is_entry)
  {
    gcc_assert(llvm_block);
    gcc_assert(context && context->curr_context);
    FnContext* const fn_context = FnContext::from(context->curr_context);
    BlockRepr* block_repr = 0;
    ValueRepr* label_decl_repr = 0;
    tree label_id_tree = NULL_TREE;
    tree label_decl_tree = NULL_TREE;
    std::string llvm_block_label = llvm_block->getName();

    // Test whether the representation has already been set-up.
    block_repr = static_cast<BlockRepr*>(context->lookup_value(llvm_block));

    if (block_repr)
      return block_repr;

    // Create a new block.
    block_repr = new BlockRepr(llvm_block);
    fn_context->register_value_repr(block_repr);

    // Create the label declaration, but not for the entry block (because it is
    // never the target of a jump).
    if (!is_entry)
      {
        location_t block_loc = UNKNOWN_LOCATION;

        label_id_tree = Tree::make_name_tree(llvm_block_label, false);
        label_decl_tree =
          Tree::declare_label_tree(block_loc, label_id_tree, 
                                   fn_context->fn_repr->get_decln(),
                                   true);
        label_decl_repr = new ValueRepr(0, label_decl_tree);
        fn_context->register_value_repr(label_decl_repr);

        // Add the label to the block
        block_repr->set_label(label_decl_repr);
      }

    return block_repr;
  }

  /* Form the final list of instructions for a basic block, after all
     code has been generated.  This handles any extra code that must
     be added to the code generated for the basic block's
     instructions.  */
  BlockRepr* 
  Parser::finalize_block_repr(Context* context ATTRIBUTE_UNUSED,
                              BlockRepr* block_repr)
  {
    if (!block_repr)
      return block_repr;
    InstrRepr* term_repr = block_repr->terminator;

    // If there's no terminator, add the extra code to the standard
    // list.
    if (!term_repr)
      {
        block_repr->instrs.append(&block_repr->extra_end_instrs);
        return block_repr;
      }

    // Iterate through the extra instructions, adding their
    // tree expressions to the terminator.
    for (ValueRepr *itr = block_repr->extra_end_instrs.front(); 
         itr != 0;
         itr = itr->next())
      {
        InstrRepr* instr_repr = static_cast<InstrRepr*>(itr);
        term_repr->add_extra_instrs(instr_repr->build());
      }

    return block_repr;
  }

  /* Convert an LLVM basic block, generating a list of instructions. This is
     the top-level function for converting basic blocks. It adds newly created
     block representation objects to the tables.
  */
  BlockRepr* 
  Parser::convert_basic_block(Context *context,
                              const llvm::Value* llvm_value)
  {
    gcc_assert(context && context->curr_context);
    ObjContext* const curr_context = context->curr_context;
    const llvm::BasicBlock *llvm_block = 
      static_cast<const llvm::BasicBlock*>(llvm_value);
    BlockRepr* block_repr = 0;
    InstrReprList *block_instr_list = 0;
    ValueRepr* label_decl_repr = 0;

    gcc_assert(curr_context);

    // Look up the LLVM block
    block_repr =
      static_cast<BlockRepr*>(context->lookup_value(llvm_block));
    gcc_assert(block_repr);

    // Test for an already converted block.
    if (block_repr->is_converted())
      return block_repr;

    // Create the label expression that appears in the block.
    label_decl_repr = block_repr->get_label();
      
    if (label_decl_repr)
      {
        location_t block_loc = UNKNOWN_LOCATION;
        tree label_tree = NULL_TREE;
        ValueRepr* label_repr = 0;

        gcc_assert(label_decl_repr->get_tree());

        label_tree = fold_build1_loc(block_loc,
                                     LABEL_EXPR, void_type_node,
                                     label_decl_repr->get_tree());

        label_repr = new InstrRepr(0, label_tree, label_tree);
        curr_context->register_value_repr(label_repr);
        block_repr->instrs.push_front(label_repr);
      }

    block_instr_list = &(block_repr->instrs);
    for(llvm::BasicBlock::const_iterator 
          itr = llvm_block->begin(),
          end = llvm_block->end();
        itr != end;
        ++itr)
      {
        const llvm::Instruction* llvm_instr = &(*itr);
        InstrRepr* instr_repr =
          convert_instruction(context, llvm_instr);
        
        block_instr_list->push_back(instr_repr);

        if (instr_repr->is_terminator)
          {
            gcc_assert(!block_repr->terminator);
            block_repr->terminator = instr_repr;
          }
      }
    
    block_repr->set_converted(true);
    return block_repr;
  }

  /*
   * Function declaration and definition.
   */

  /* Apply modifiers to a function. */
  FunctionRepr*
  Parser::apply_function_modifiers(Context *context ATTRIBUTE_UNUSED, 
                                   const llvm::Function* llvm_function,
                                   FunctionRepr* fn_repr)
  { 
    gcc_assert(fn_repr && fn_repr->get_decln());
    tree fn_decl_tree = fn_repr->get_decln();

    // Set linkage.
    const llvm::GlobalValue::LinkageTypes 
      llvm_linkage = llvm_function->getLinkage();

    switch (llvm_linkage)
      {
      case llvm::GlobalValue::ExternalLinkage:
      case llvm::GlobalValue::AvailableExternallyLinkage:
        TREE_PUBLIC(fn_decl_tree) = true;
        break;
      case llvm::GlobalValue::InternalLinkage:
      case llvm::GlobalValue::PrivateLinkage:
        TREE_PUBLIC(fn_decl_tree) = false;
        break;
      case llvm::GlobalValue::WeakAnyLinkage:
      case llvm::GlobalValue::WeakODRLinkage:
        DECL_WEAK(fn_decl_tree) = true;
        TREE_PUBLIC(fn_decl_tree) = true;
        break;
      case llvm::GlobalValue::DLLImportLinkage:
        DECL_DLLIMPORT_P(fn_decl_tree) = true;
      default:
        // By default, use externally visible linkage
        TREE_PUBLIC(fn_decl_tree) = true;
        break;
      }

    return fn_repr;
  }

  /* Make a declaration for an LLVM intrinsic function. Doesn't
     register the declaration.*/
  FunctionRepr* 
  Parser::make_intrinsic_decln(Context *context, 
                               const llvm::Function *llvm_function)
  {
    /*
     * Useful information about LLVM intrinsics and how they might map
     * to GCC built-ins can be found in file llvm/Intrinsics.h and
     * llvm/Intrinsics.gen.
     */
    gcc_assert(llvm_function);
    gcc_assert(context && context->curr_context);

    const unsigned llvm_intrinsic_id  = llvm_function->getIntrinsicID();
    location_t fn_location = UNKNOWN_LOCATION;
    FunctionRepr* fn_repr = 0;
    tree fndecl_tree = NULL_TREE;

    // Check for a existing declaration.
    fn_repr = static_cast<FunctionRepr*>(context->lookup_value(llvm_function));
    if (fn_repr)
      return fn_repr;

    // The easily mapped intrinsic->builtin id.
    switch (llvm_intrinsic_id)
      {
      case llvm::Intrinsic::vastart:
        fndecl_tree = Tree::builtin_decl(BUILT_IN_VA_START);
        break;
      case llvm::Intrinsic::vaend:
        fndecl_tree = Tree::builtin_decl(BUILT_IN_VA_END);
        break;
      case llvm::Intrinsic::vacopy:
        fndecl_tree = Tree::builtin_decl(BUILT_IN_VA_COPY);
        break;
      case llvm::Intrinsic::returnaddress:
        fndecl_tree = Tree::builtin_decl(BUILT_IN_RETURN_ADDRESS);
        break;
      case llvm::Intrinsic::frameaddress:
        fndecl_tree = Tree::builtin_decl(BUILT_IN_FRAME_ADDRESS);
        break;
      case llvm::Intrinsic::stacksave:
        fndecl_tree = Tree::builtin_decl(BUILT_IN_STACK_SAVE);
        break;
      case llvm::Intrinsic::stackrestore:
        fndecl_tree = Tree::builtin_decl(BUILT_IN_STACK_RESTORE);
        break;
      case llvm::Intrinsic::prefetch:
        fndecl_tree = Tree::builtin_decl(BUILT_IN_PREFETCH);
        break;

      case llvm::Intrinsic::memset:
        fndecl_tree = Tree::builtin_decl(BUILT_IN_MEMSET);
        break;
      case llvm::Intrinsic::memcpy:
        fndecl_tree = Tree::builtin_decl(BUILT_IN_MEMCPY);
        break;
      case llvm::Intrinsic::memmove:
        fndecl_tree = Tree::builtin_decl(BUILT_IN_MEMMOVE);
        break;

      case llvm::Intrinsic::sqrt:
        fndecl_tree = Tree::builtin_decl(BUILT_IN_SQRT);
        break;
      case llvm::Intrinsic::powi:
        fndecl_tree = Tree::builtin_decl(BUILT_IN_POW);
        break;
      case llvm::Intrinsic::sin:
        fndecl_tree = Tree::builtin_decl(BUILT_IN_SIN);
        break;
      case llvm::Intrinsic::cos:
        fndecl_tree = Tree::builtin_decl(BUILT_IN_COS);
        break;
      case llvm::Intrinsic::pow:
        fndecl_tree = Tree::builtin_decl(BUILT_IN_POW);
        break;
      case llvm::Intrinsic::exp:
        fndecl_tree = Tree::builtin_decl(BUILT_IN_EXP);
        break;
      case llvm::Intrinsic::log:
        fndecl_tree = Tree::builtin_decl(BUILT_IN_LOG);
        break;
      case llvm::Intrinsic::ctlz:
        fndecl_tree = Tree::builtin_decl(BUILT_IN_CTZL);
        break;
      case llvm::Intrinsic::cttz:
        fndecl_tree = Tree::builtin_decl(BUILT_IN_CTZ);
        break;

      case llvm::Intrinsic::trap:
        fndecl_tree = Tree::builtin_decl(BUILT_IN_TRAP);
        break;
      case llvm::Intrinsic::objectsize:
        fndecl_tree = Tree::builtin_decl(BUILT_IN_OBJECT_SIZE);
        break;
      default:
        fndecl_tree = NULL_TREE;
        break;
      }

    if (fndecl_tree)
      {
        tree fn_type_tree = TREE_TYPE(fndecl_tree);
        tree fnptr_type_tree = build_pointer_type(TREE_TYPE(fndecl_tree));
        tree fnptr_tree = build1_loc(fn_location, ADDR_EXPR,
                                     fnptr_type_tree, fndecl_tree);

        fn_repr = new FunctionRepr(llvm_function, fnptr_tree);
        context->register_value_repr(fn_repr);

        fn_repr->fn_type = new FuncTypeRepr(0, fn_type_tree);
        fn_repr->decln_tree = fndecl_tree;
      }

    return fn_repr;
  }

  /* Make a declaration for an LLVM function. */
  FunctionRepr* 
  Parser::make_function_decln(Context *context, 
                              const llvm::Function *llvm_function)
  {
    gcc_assert(llvm_function);
    gcc_assert(context && context->curr_context);

    FunctionRepr* fn_repr = 0;
    location_t fn_location = UNKNOWN_LOCATION;
    tree fn_decl_tree = NULL_TREE;
    TypeRepr* fn_type_repr = 0;
    tree fn_ref_tree = NULL_TREE;
    tree fn_id_tree = NULL_TREE;
    BlockReprStdList block_list;

    // Check for a existing declaration.
    fn_repr = static_cast<FunctionRepr*>(context->lookup_value(llvm_function));
    if (fn_repr)
      return fn_repr;

    // Handle intrinsics.
    if (llvm_function->isIntrinsic())
      {
        fn_repr = make_intrinsic_decln(context, llvm_function);

        if (!fn_repr)
          {
            std::string fn_name_str = llvm_function->getName();
            warning_at(fn_location, 0,
                       "unknown intrinsic function %s", 
                       fn_name_str.c_str());
          }
        return fn_repr;
      }

    fn_repr = new FunctionRepr(llvm_function, NULL_TREE);
    context->register_value_repr(fn_repr);

    // Convert the function type
    fn_type_repr = convert_type(context, llvm_function->getFunctionType());
    gcc_assert(fn_type_repr && fn_type_repr->get_tree());
    gcc_assert(fn_type_repr->get_kind() == TypeRepr::FUNC);

    fn_repr->fn_type = static_cast<FuncTypeRepr*>(fn_type_repr);

    // Create the declaration.
    std::string fn_id_str = llvm_function->getName();
    fn_id_tree = Tree::make_name_tree(fn_id_str, false);
    fn_decl_tree = build_decl(fn_location, FUNCTION_DECL,
                              fn_id_tree,
                              fn_type_repr->get_tree());
    TREE_USED(fn_decl_tree) = 1;
    TREE_PUBLIC(fn_decl_tree) = 1;
    TREE_STATIC(fn_decl_tree) = 1;
    DECL_EXTERNAL(fn_decl_tree) = 0;

    // Set the function context.
    DECL_CONTEXT(fn_decl_tree) = context->unit_context.unit_decln_tree;

    // Set-up the tree used to refer to the function.
    fn_ref_tree = build1(ADDR_EXPR, 
                         build_pointer_type(fn_type_repr->get_tree()),
                         fn_decl_tree);
    fn_repr->set_tree(fn_ref_tree);
    fn_repr->decln_tree = fn_decl_tree;

    // Apply modifiers.
    fn_repr = apply_function_modifiers(context, llvm_function, fn_repr);

    return fn_repr;
  }

  /* Convert an LLVM function declaration. This is used to declare
     functions before converting the function definitions. */
  FunctionRepr* 
  Parser::convert_function_decln(Context *context, 
                                 const llvm::Value* llvm_value)
  {
    // Sanity checks.
    gcc_assert(llvm_value);
    gcc_assert(context && context->curr_context == 0);

    FnContext fn_context;
    const llvm::Function *llvm_function =
      static_cast<const llvm::Function*>(llvm_value);
    FunctionRepr* fn_repr = 0;

    // Check for a existing declaration.
    fn_repr = static_cast<FunctionRepr*>(context->lookup_value(llvm_function));
    if (fn_repr)
      return fn_repr;
    
    // Set-up a local context.
    context->curr_context = &fn_context;

    // Make the declaration.
    if (!fn_repr)
      fn_repr = make_function_decln(context, llvm_function);

    // Reset the local context.
    context->curr_context = 0;
    return fn_repr;
  }

  /* Convert an LLVM function, returning a GCC fndecl.  */
  FunctionRepr* 
  Parser::convert_function(Context *context, 
                           const llvm::Value* llvm_value)
  {
    // Sanity checks.
    gcc_assert(llvm_value);
    gcc_assert(context && context->curr_context == 0);

    FnContext fn_context;
    const llvm::Function *llvm_function =
      static_cast<const llvm::Function*>(llvm_value);
    location_t fn_location = UNKNOWN_LOCATION;
    FunctionRepr* fn_repr = 0;
    FuncTypeRepr* fn_type_repr = 0;
    tree fn_decl_tree = NULL_TREE;
    tree fn_scope_tree = NULL_TREE;
    tree fn_ret_val_tree = NULL_TREE;
    tree fn_ret_type_tree = NULL_TREE;
    tree fn_param_list = NULL_TREE;
    const llvm::BasicBlock* llvm_entry_block = 0;
    InstrReprList fn_instr_list;
    BlockRepr* block_repr = 0;
    BlockReprStdList block_list;

    context->curr_context = &fn_context;

    // Check for a existing declaration.
    fn_repr = static_cast<FunctionRepr*>(context->lookup_value(llvm_function));

    // Non-intrinsic functions.
    if (!fn_repr)
      fn_repr = make_function_decln(context, llvm_function);

    // Nothing more to do for a simple declaration.
    if (llvm_function->isDeclaration())
      {
        context->curr_context = 0;
        return fn_repr;
      }
    gcc_assert(fn_repr);
    gcc_assert(fn_repr->fn_type && fn_repr->fn_type->get_tree());

    // Function definitions.
    fn_decl_tree = fn_repr->get_decln();
    fn_type_repr = fn_repr->fn_type;

    // Set the function as the current definition.
    fn_context.set_function(fn_repr);

    // First, the function scope hierarchy. For LLVM-IR, all variables
    // go into the same scope.

    fn_scope_tree = build_block(NULL_TREE, NULL_TREE, fn_decl_tree, NULL_TREE);
    TREE_USED(fn_scope_tree) = true;
    DECL_INITIAL(fn_decl_tree) = fn_scope_tree;
    fn_repr->scope_tree = fn_scope_tree;

    // Create the return value.
    gcc_assert(fn_type_repr->get_tree());
    if (!fn_type_repr->get_return_type())
      fn_ret_type_tree = void_type_node;
    else
      {
        const llvm::Type* llvm_fn_ret_type = fn_type_repr->get_return_type();
        TypeRepr* fn_ret_type_repr = convert_type(context, llvm_fn_ret_type);
        gcc_assert(fn_ret_type_repr && fn_ret_type_repr->get_tree());
        fn_ret_type_tree = fn_ret_type_repr->get_tree();
      }

    fn_ret_val_tree = build_decl(fn_location,
                                 RESULT_DECL, NULL_TREE,
                                 fn_ret_type_tree);
    DECL_CONTEXT(fn_ret_val_tree) = fn_decl_tree;
    DECL_RESULT(fn_decl_tree) = fn_ret_val_tree;

    fn_repr->result = new ValueRepr(0, fn_ret_val_tree);
    context->curr_context->register_value_repr(fn_repr->result);

    // Set-up the parameters.
    fn_param_list = NULL_TREE;
    for(llvm::Function::const_arg_iterator 
          itr = llvm_function->arg_begin(),
          end = llvm_function->arg_end();
        itr != end;
        ++ itr)
      {
        const llvm::Argument* llvm_param = &(*itr);
        tree param_tree = NULL_TREE;
        ValueRepr* param_repr = convert_parameter(context, llvm_param);

        gcc_assert(param_repr);
        param_tree = param_repr->get_tree();

        DECL_CONTEXT(param_tree) = fn_decl_tree;
        fn_param_list = chainon(fn_param_list, param_tree);
      }
    DECL_ARGUMENTS(fn_decl_tree) = fn_param_list;

    // Record which is the first block in the function.
    llvm_entry_block = &(llvm_function->getEntryBlock());

    // Iterate through the function basic blocks, setting up the block
    // representations. This sets up the representation to use when
    // the block is refered to, e.g. by a goto, but does not convert
    // the block.
    block_repr = setup_block_repr(context, llvm_entry_block, true);
    // Also set up block_list with the entry block as the first entry.
    block_list.push_front(block_repr);

    for (llvm::Function::const_iterator 
           itr = llvm_function->begin(),
           end = llvm_function->end();
         itr != end;
         ++ itr)
      {
        const llvm::BasicBlock* llvm_block = &(*itr);

        if (llvm_block == llvm_entry_block)
          continue;

        block_repr = setup_block_repr(context, llvm_block, false);
        // Add to the end of the list of blocks.
        block_list.push_back(block_repr);
      }

    // Iterate through the function basic blocks, converting the
    // instructions. The entry block will be at the start of the list.
    fn_repr->stmt_list = NULL_TREE;
    gcc_assert((block_list.front())->get_block() == llvm_entry_block);

    for (BlockReprStdList::iterator
           itr = block_list.begin(),
           end = block_list.end();
         itr != end;
         ++ itr)
      {
        BlockRepr* itr_repr = *itr;
        const llvm::BasicBlock* llvm_block = itr_repr->get_block();

        block_repr = convert_basic_block(context, llvm_block);
        fn_instr_list.append(&(block_repr->instrs));
      }
        
    // Iterate through the function basic blocks, finalizing the
    // instructions.  This makes sure that any extra code added to the
    // basic block, by an instruction in a different block, is
    // generated in the right place.
    for (BlockReprStdList::iterator 
           itr = block_list.begin(),
           end = block_list.end();
         itr != end;
         ++ itr)
      {
        BlockRepr* block_repr = *itr;
        finalize_block_repr(context, block_repr);
      }

    // Add the instructions from the blocks to the function list.
    for (ValueRepr *itr = fn_instr_list.front(),
           *next_itr = 0;
         itr != 0;
         itr = next_itr)
      {
        InstrRepr* instr_repr = static_cast<InstrRepr*>(itr);
        tree instr_tree = 0;
        next_itr = itr->next();

        instr_tree = instr_repr->build();
        append_to_statement_list(instr_tree, &(fn_repr->stmt_list));
          
        fn_instr_list.remove(instr_repr);
      }
    fn_instr_list.clear();
  
    // Finish up
    tree binding_tree = build3(BIND_EXPR,
                               void_type_node,
                               BLOCK_VARS(fn_scope_tree),
                               NULL_TREE,
                               fn_scope_tree);
    TREE_SIDE_EFFECTS(binding_tree) = 1;

    BIND_EXPR_BODY(binding_tree) = fn_repr->stmt_list;
    DECL_SAVED_TREE(fn_decl_tree) = binding_tree;

    gcc_assert(fn_repr->get_tree());
    context->curr_context = 0;
    fn_context.reset();
    return fn_repr;
  }

} // namespace llvmir
